// File: jmcomic-core/src/main/java/dev/jukomu/common/postman/proxy/RedirectPostman.java
package dev.jukomu.common.postman.proxy;

import dev.jukomu.common.postman.Postman;
import dev.jukomu.common.util.net.IResponse;

import java.util.Map;

/**
 * 对应Python的RedirectPostman，一个Postman代理，用于捕获重定向URL。
 * 它会禁用自动重定向，并手动处理301/302状态码以获取Location头。
 */
public class RedirectPostman extends PostmanProxy {

    /**
     * 构造函数。
     *
     * @param postman 被代理的Postman实例。
     */
    public RedirectPostman(Postman postman) {
        super(postman);
    }

    /**
     * 执行请求并捕获重定向URL。
     *
     * @param requestFunction 执行实际请求的函数
     * @param url             请求URL
     * @param kwargs          请求参数Map
     * @return 重定向后的URL字符串
     * @throws Exception 如果请求失败或遇到不支持的重定向状态码
     */
    protected String requestAndCatchRedirect(RetryPostman.RequestFunction requestFunction, String url, Map<String, Object> kwargs) throws Exception {
        Map<String, Object> actualKwargs = kwargs != null ? new java.util.HashMap<>(kwargs) : new java.util.HashMap<>();
        actualKwargs.put("allow_redirects", false); // 禁用自动重定向

        String currentUrl = url;
        while (true) {
            IResponse resp = requestFunction.execute(currentUrl, actualKwargs);
            int statusCode = resp.getHttpCode();

            if (statusCode == 301 || statusCode == 302) {
                // 301 (Moved Permanently) 或 302 (Found/Moved Temporarily)
                String redirectUrl = resp.getRedirectUrl();
                if (redirectUrl == null || redirectUrl.isEmpty()) {
                    throw new Exception("Redirect status " + statusCode + " but no Location header found for URL: " + currentUrl);
                }
                currentUrl = redirectUrl;
                if (statusCode == 302) {
                    // Python的RedirectPostman在302时会break并返回Location
                    // 如果我们希望它追踪302直到非重定向或301，则需要修改逻辑
                    // 原始Python代码在302时会break，然后返回 Location。
                    // 似乎只追踪301，而302则直接返回 Location。
                    // 我们这里为了统一，遇到301/302都获取Location，然后返回。
                    return redirectUrl;
                }
                // 对于301，继续循环，用新的URL再次请求
                // return redirectUrl; // If we want to return on 301 immediately. Python continue loop.
            } else if (statusCode == 200) {
                // If it's a 200 OK, it means no redirect happened for the last request,
                // or the target is reached. Return the final URL.
                return resp.getUrl();
            } else {
                throw new UnsupportedOperationException(String.format(
                        "Unsupported redirect status code: %d for URL: %s. Response: %s",
                        statusCode, currentUrl, resp.getText()
                ));
            }
        }
    }

    @Override
    public IResponse get(String url, Map<String, Object> kwargs) throws Exception {
        // RedirectPostman 的 get/post 方法返回的是重定向后的 URL，而不是 IResponse 对象。
        // 这与Postman接口的定义不符。需要修改接口或者将此代理类设计为只返回URL的特殊Postman。
        // 鉴于Python原始实现，它确实返回的是字符串URL。
        // 为了兼容Postman接口，我们让它返回一个特殊响应，或抛出异常。
        // 根据Python实现，如果url是fixurl，返回Location。否则会返回原始响应。
        // 由于原始Python的RedirectPostman的get/post方法最终是返回一个URL，而不是Resp，
        // 这里需要修改为返回IResponse或者特殊处理。
        // 考虑到接口一致性，如果它只返回URL，那么它的get/post就不应该声明为返回IResponse。
        // 更好的设计是：增加一个专门的 `getRedirectedUrl(String url, ...)` 方法。
        // 为了和Python保持一致，且Postman接口返回的是IResponse，
        // 我们可以让它在遇到重定向时，获取到最终URL后，再用这个URL发起一次真实的请求并返回IResponse。
        // 另一种方式是，仅返回重定向URL，但这破坏了Postman接口。
        // 回到Python代码：它在RedirectPostman中request方法返回的是 self.headers['Location']
        // 所以它确实不是返回响应对象。

        // 鉴于此，此PostmanProxy不再返回IResponse，而是返回String。
        // 这将与IResponse接口冲突，需要特殊处理。
        // 最佳实践：RedirectPostman应该返回IResponse，并在IResponse中包含重定向信息。
        // 或者，它是一个特殊的Postman，不直接实现get/post，而是提供一个类似getRedirectedUrl的方法。
        //
        // 按照Python的实现，RedirectPostman的get和post方法返回的是最终的redirect_url字符串。
        // 这与Postman接口返回IResponse的约定冲突。
        // 为解决此冲突，且遵循Python语义，我们在此处抛出UnsupportedOperationException，
        // 或者返回一个表示URL的特殊IResponse（不太合理）。
        // 更合理的方案是，如果需要获取重定向URL，客户端调用专门的方法，而不是get/post。
        //
        // 暂时按照Python代码逻辑，get/post直接返回重定向URL，但由于类型不匹配，
        // 只能在此处抛出异常或返回null。这需要JmcomicClient做特殊处理。
        //
        // 鉴于此处的类型冲突，RedirectPostman不应该直接实现Postman接口的get/post方法，
        // 而是提供一个单独的公共方法，比如 `resolveRedirectUrl`。
        // 如果要保留get/post的签名，它的行为就必须是返回IResponse。
        // 如果它返回最终的IResponse，那么 `actualKwargs.put("allow_redirects", false)`
        // 并且在循环中再次调用 `postman.get/post` 直到获得200或非3xx响应。

        // 修改：让RedirectPostman返回最终的IResponse，而不是URL字符串，以符合Postman接口。
        // 也就是说，它会一直跟随重定向直到获得非3xx响应。
        Map<String, Object> actualKwargs = kwargs != null ? new java.util.HashMap<>(kwargs) : new java.util.HashMap<>();
        actualKwargs.put("allow_redirects", false); // 禁用自动重定向，手动处理

        String currentUrl = url;
        IResponse finalResponse = null;
        for (int i = 0; i < 10; i++) { // Limit redirect chain to prevent infinite loops
            finalResponse = super.get(currentUrl, actualKwargs);
            int statusCode = finalResponse.getHttpCode();

            if (statusCode >= 300 && statusCode < 400) { // Is a redirect
                String redirectUrl = finalResponse.getRedirectUrl();
                if (redirectUrl == null || redirectUrl.isEmpty()) {
                    throw new Exception("Redirect status " + statusCode + " but no Location header found for URL: " + currentUrl);
                }
                currentUrl = redirectUrl;
                // If the original Python code for 302 break, then we need to break too
                // If it's a 302 (Found) and Python breaks, it means it returns the *current* response which is a 302.
                // But the Python's method returns a URL string, so it's not the IResponse.
                // Re-reading Python's code for RedirectPostman: it does return Location.
                // This means this proxy should transform the behavior.
                // For now, let's make it follow redirects until a non-redirect status is met.
                // If the Python logic specifically wants the *first* redirect URL, this needs redesign.
                // Assuming "catch redirecting" means "follow until non-redirect".
            } else {
                break; // Not a redirect, return this response
            }
        }
        if (finalResponse == null) {
            throw new Exception("RedirectPostman failed to get a response after following redirects.");
        }
        return finalResponse;
    }

    @Override
    public IResponse post(String url, Map<String, Object> kwargs) throws Exception {
        Map<String, Object> actualKwargs = kwargs != null ? new java.util.HashMap<>(kwargs) : new java.util.HashMap<>();
        actualKwargs.put("allow_redirects", false); // 禁用自动重定向，手动处理

        String currentUrl = url;
        IResponse finalResponse = null;
        for (int i = 0; i < 10; i++) { // Limit redirect chain
            finalResponse = super.post(currentUrl, actualKwargs);
            int statusCode = finalResponse.getHttpCode();

            if (statusCode >= 300 && statusCode < 400) { // Is a redirect
                String redirectUrl = finalResponse.getRedirectUrl();
                if (redirectUrl == null || redirectUrl.isEmpty()) {
                    throw new Exception("Redirect status " + statusCode + " but no Location header found for URL: " + currentUrl);
                }
                currentUrl = redirectUrl;
            } else {
                break;
            }
        }
        if (finalResponse == null) {
            throw new Exception("RedirectPostman failed to get a response after following redirects.");
        }
        return finalResponse;
    }

    @Override
    public Postman copy() {
        return new RedirectPostman(postman.copy());
    }
}